#encoding:utf8

import os
import time
import flag
import traceback
from datetime import datetime
from threading import Thread, Lock

from scheduler.triggers.util import convert_to_datetime
from webserver.models import Tasker
from scheduler.JobTask import TaskerWrapper
from utils.flush_transaction import commit_transaction, close_connection

from config import CONCURRENCE, CONCURRENCE_INTERVAL
from utils.Logger import get_logger
from utils.SimpleLock import WithLock, simple_lock

logger = get_logger("controler", handlers=['rtfile'], path="./log/controlerlog/")


class TaskerPool(object):
    # 抽离出来方便形成全局变量
    def __init__(self, tasker_dict):
        # dict(id=jobtask, ...)
        self.tasker_dict = tasker_dict
        # 当需要在外部终止或者启动某一个任务的时候, 防止不一致现象

    def reset(self, new_tasker_dict):
        self.tasker_dict = {}
        for k, v in new_tasker_dict.iteritems():
            self.tasker_dict[k] = v

    def copy(self):
        # not use deepcopy
        d = {}
        for k, v in self.tasker_dict.iteritems():
            d[k] = v
        return d

    def add_task(self, task):
        # task, model instance
        id = task.id
        with WithLock(simple_lock, timeout=60):
            if id not in self.tasker_dict:
                self.tasker_dict[id] = TaskerWrapper(task)
            return self.tasker_dict[id]

    def start_task(self, id):
        not_exist_flag = False
        with WithLock(simple_lock, timeout=60):
            if id not in self.tasker_dict:
                not_exist_flag = True
            else:
                jobtask = self.tasker_dict[id]
                jobtask.run_instance()
        if not_exist_flag:
            raise Exception("task with id[%s] does not exist"%id)

    def shutdown_task(self, id):
        not_exist_flag = False
        with WithLock(simple_lock, timeout=60):
            if id not in self.tasker_dict:
                not_exist_flag = True
            else:
                jobtask = self.tasker_dict[id]
                jobtask.shutdown()
                return "running: {}".format(jobtask.is_running)
        if not_exist_flag:
            raise Exception("task with id[%s] does not exist" % id)


tasker_pool = TaskerPool({})


def init_tasker_pool():
    global tasker_pool
    taskers = Tasker.objects.all().filter(status__gt=-10).all()
    for task in taskers:
        tasker_pool.add_task(task)

try:
    init_tasker_pool()
except:
    pass


def start():
    while 1:
        job_running_num = 0
        # 读取和筛选 数据库中 表任务信息
        with WithLock(simple_lock, timeout=60):
            new_task_dict = {}  # 用来更新当前池
            logger.debug("<tasker_pool.tasker_dict number>: {}, pid: {}".format(len(tasker_pool.tasker_dict), os.getpid()))
            try:
                tasker_dict = tasker_pool.copy()    # use copy but not deepcopy
                logger.info("taskers number: {}".format(len(tasker_dict)))
                # commit_transaction()
                # close_connection()
                taskers = Tasker.objects.all().filter(status__gt=-10).all()
                # logger.info(">>> taskers length: {0}".format(len(taskers)))
                # 对每个任务 计算调度信息,建立调度池
                for task in taskers:
                    id = task.id
                    jobtask = tasker_dict.pop(id, None)

                    # 如果job在运行, 那么不进行替换 ;否则全部替换,因为任务可能会有更改
                    if not (jobtask is not None and jobtask.is_running):
                        jobtask = TaskerWrapper(task)

                    new_task_dict[id] = jobtask
                    logger.debug("DEBUG :: {0}, is running: {1}".format(id, jobtask.is_running))
                logger.debug("new_task_dict length: {}".format(len(new_task_dict)))
                # 处理已经下线或者删除的任务
                for id, jobtask in tasker_dict.iteritems():
                    logger.debug("job[{}] is off-line".format(id))
                    if jobtask.is_running:
                        jobtask.shutdown()

                # 针对更新的任务
                for id, jobtask in new_task_dict.iteritems():
                    if jobtask.is_running and jobtask.status == 2:  # 表示更新
                        logger.warn("job[{}] is updated, so shutdown it".format(id))
                        jobtask.shutdown()
                        # time.sleep(1)
                        logger.error("job[{}] is still running".format(id))
                        # jobtask.status = 0

                # 统计现在正在运行的程序个数
                for id, jobtask in new_task_dict.iteritems():
                    if jobtask.is_running:
                        job_running_num += 1

                # job task's last fire time
                for id, jobtask in new_task_dict.iteritems():
                    if jobtask.last_fire_time is None:
                        jobtask.last_fire_time = datetime.now()

                # 处理有效任务
                logger.debug("new task dict size: [{0}], job running num: [{1}]".format(len(new_task_dict), job_running_num))
                for id, jobtask in new_task_dict.iteritems():
                    if job_running_num >= CONCURRENCE:
                        break
                    now = convert_to_datetime(datetime.now())
                    last_fire_time = convert_to_datetime(jobtask.last_fire_time)

                    fire_time = jobtask.get_next_fire_time(last_fire_time, last_fire_time)
                    logger.debug("id: {}, last_fire_time: {}, next: {}, now:{}, job_start: {}".format(
                        id, last_fire_time, fire_time, now, jobtask.is_running))
                    if fire_time and now > fire_time and not jobtask.is_running:
                        # trigger job
                        # print("run_instance ... id: {}".format(id, ))
                        logger.debug("start task [{0}], job is running: [{1}]".format(id, jobtask.is_running))
                        jobtask.run_instance()
                        logger.debug("job instance is running ? : [{}]".format(jobtask.is_running))
                        job_running_num += 1
                #
            except Exception, ex:
                traceback.print_exc()
                simple_lock.release()
            tasker_pool.reset(new_task_dict)
            logger.warn("tasker_pool.tasker_dict :: {}".format(len(tasker_pool.tasker_dict)))
            if new_task_dict:
                logger.warn("new task dict :: {}".format(new_task_dict.values()[0].is_running))
        time.sleep(CONCURRENCE_INTERVAL)


def run():
    th = Thread(name="controler", target=start)
    th.setDaemon(True)
    th.start()

# print >> sys.stdout, "start run ... "
# need global variables

# if not flag.RUN_CONTROLER:
#     print("start controler running ... ")
#     run()
#     flag.RUN_CONTROLER = True

"""
1, 多进程无法用模块级别的变量来实现进程之间的通信;
2, 多进程也无法更改进程外的变量, 因为不管是lock, value, manager, 都是需要用参数传递的.
3, 只能利用外部资源,
    3.1 文件, 如何实现服务停掉,重启时候的自动识别?
    3.2 webapi接口, 感觉应该可以.
"""

